package cn.tedu.mall.front.service.impl;


import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.front.service.IFrontCategoryService;
import cn.tedu.mall.pojo.front.entity.FrontCategoryEntity;
import cn.tedu.mall.pojo.front.vo.FrontCategoryTreeVO;
import cn.tedu.mall.pojo.product.vo.CategoryStandardVO;
import cn.tedu.mall.product.service.front.IForFrontCategoryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class FrontCategoryServiceImpl implements IFrontCategoryService {

    @DubboReference
    private IForFrontCategoryService dubboCategoryService;

    @Autowired
    private RedisTemplate redisTemplate;

    public static final String CATEGORY_TREE_KEY = "category_tree";

    @Override
    public FrontCategoryTreeVO categoryTree() {
        // 方法中先检查Redis中是否保存了三级分类树对象
        if(redisTemplate.hasKey(CATEGORY_TREE_KEY)){
            // redis中如果已经有了这个key直接获取即可,
            // 也就是说这个【(FrontCategoryTreeVO<FrontCategoryEntity>) redisTemplate.boundValueOps(CATEGORY_TREE_KEY).get()】返回的是
            // 封装了【private List<T> categories;】成员属性的Object类
            FrontCategoryTreeVO<FrontCategoryEntity> treeVO = (FrontCategoryTreeVO<FrontCategoryEntity>) redisTemplate.boundValueOps(CATEGORY_TREE_KEY).get();
            return treeVO;
        }
        // Redis中没有三级分类树信息,表示本次访问可以是首次访问
        // 就要进行连接数据库查询数据后,构建三级分类树结构,再保存到Redis的业务流程
        // dubbo调用查询所有分类对象的方法
        List<CategoryStandardVO> categoryStandardVOS = dubboCategoryService.getCategoryList();
        // 记住CategoryStandardVO是没有children属性的,FrontCategoryEntity是有的!
        // 下面需要编写一个方法,将子分类对象保存到对应的父分类对象的children属性中
        // 大概思路就是先将CategoryStandardVO转换为FrontCategoryEntity类型,然后再将父子分类关联
        // 整个转换和关联的过程比较复杂,我们编写一个方法来完成
        FrontCategoryTreeVO<FrontCategoryEntity> treeVO = initTree(categoryStandardVOS);

        // 上面方法完成了三级分类树的构建,下面要将treeVO保存到Redis中set(数据，保存数值，保存单位);
        redisTemplate.boundValueOps(CATEGORY_TREE_KEY).set(treeVO,1, TimeUnit.MINUTES);
        return treeVO;
    }

    private FrontCategoryTreeVO<FrontCategoryEntity> initTree(List<CategoryStandardVO> categoryStandardVOS) {
        // 第一步:
        // 确定所有分类的父分类id
        // 以父分类的id为Key,以子分类对象为value保存在一个Map中
        // 一个父分类可以包含多个子分类对象,所以这个map的value是个list
        Map<Long,List<FrontCategoryEntity>> map = new HashMap<>();
        log.info("准备构建的三级分类树对象数量为:{}",categoryStandardVOS.size());
        Long parentId;
        // 遍历数据库查询出来的所有分类集合对象
        for (CategoryStandardVO categoryStandardVO : categoryStandardVOS) {
            // 获取当前分类对象的父分类id,用作map元素的key值(如果父分类id为0,就是一级分类)
            parentId = categoryStandardVO.getParentId();
            // 因为CategoryStandardVO对象没有children属性,不能保存关联的子分类对象
            // 所以要将categoryStandardVO中的值赋值给能保存children属性的FrontCategoryEntity对象
            FrontCategoryEntity frontCategoryEntity = new FrontCategoryEntity();
            BeanUtils.copyProperties(categoryStandardVO,frontCategoryEntity);

            // 判断这个父分类id是否已经存在于map
            if(!map.containsKey(parentId)){
                // 如果map中没有当前遍历对象父分类id作为key的元素
                // 那么就要新建这个元素,就要确定key和value
                // key就是parentId,value是一个list,要实例化,而且list中还要保存当前正在遍历的对象
                List<FrontCategoryEntity> value = new ArrayList<>();
                value.add(frontCategoryEntity);
                map.put(parentId,value);
            }else {
                // 如果map有，则通过key获取value值，再往value值是List类型的对象添加【frontCategoryEntity】
                map.get(parentId).add(frontCategoryEntity);
            }
        }

        // 第二步:
        // 将子分类对象添加到对应的父分类对象的childrens属性中
        // 先获取所有一级分类对象,也就是父分类id为0的对象
        // Key是Long类型,一定要写0L
        List<FrontCategoryEntity> firstLevels = map.get(0L);
        if(firstLevels == null || firstLevels.isEmpty()){
            String message = "一级分类不存在";
            throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR,message);
        }

        for (FrontCategoryEntity firstLevel : firstLevels) {
            Long secondParentId = firstLevel.getId();
            List<FrontCategoryEntity> secondLevels = map.get(secondParentId);

            if(secondLevels == null || secondLevels.isEmpty()){
                log.warn("当前分类没有二级分类内容:{}",secondParentId);
                continue;
            }

            for (FrontCategoryEntity secondLevel : secondLevels) {
                Long thirdParentId = secondLevel.getId();
                List<FrontCategoryEntity> thirdLevels = map.get(thirdParentId);
                if (thirdLevels == null || thirdLevels.isEmpty()){
                    log.warn("当前分类没有三级分类内容:{}",thirdParentId);
                    continue;
                }
                secondLevel.setChildrens(thirdLevels);
            }
            firstLevel.setChildrens(secondLevels);
        }
        // 到此为止,所有的分类对象都应该确认了自己和父\子分类对象的关联关系
        // 最后我们要将一级分类的集合firstLevels,
        // 赋值给FrontCategoryTreeVO<FrontCategoryEntity>的list属性
        // 实例化对象
        FrontCategoryTreeVO<FrontCategoryEntity> treeVO = new FrontCategoryTreeVO<>();//泛型，FrontCategoryTreeVO<T>的T要与成员属性List<T>的T保持一致。
        treeVO.setCategories(firstLevels);
        return treeVO;
    }
}
